<?php

/**
 * Abstract Filters class
 * @author greenogre <ogre.green@gmail.com>
 * @since 17.05.2012
 * @copyright 2012. greenogre
 * @licence LGPL
 */
class Green_FiltersAbstract implements Iterator {

    /**
     * filters array
     * @var array
     */
    protected $_filters = array();


    /**
     * !!! class name !!! REQUIRED in extended classes
     * uses for exception messages
     * example:
     * class Example_Company_Filters extends Green_FiltersAbstract {
     *      protected $_className = 'Example_Company_Filters';
     *      ...
     * }
     * @var string
     */
    protected $_className = null;

    /**
     *
     * magic method.
     *
     * examples:
     *  getCategory($value)
     *  setCategory()
     *  removeCategory()
     *
     * @param string $method
     * @param array $params
     * @throws Zend_Exception
     * @return BisApi_Filters
     */
    public function __call($method, $params) {

        if ($this->_className === null) {
            throw new Green_Filters_Exception('class property _className must be declared in extended classes');
        }

        // method name must be started with set, get or remove (setCategory, getCategory, removeCategory)
        $methodName = substr($method, 0, 3);

        if (in_array($methodName, array('get', 'set')) == false) {
            $methodName = substr($method, 0, 6);

            if ($methodName != 'remove') {
                throw new Green_Filters_Exception("unknown '{$this->_className}' method '{$method}'", Green_Filters_Exception::UNKNOWN_FILTER_METHOD);
            }
        }

        // get filter value
        if ($methodName == 'get') {
            $filterName = substr($method, 3);

            // get constant name and test constant
            if (false === $constantName = $this->_getConstantName($filterName)) {
                throw new Green_Filters_Exception("unknown '{$this->_className}' with name = '{$filterName}'", Green_Filters_Exception::UNKNOWN_FILTER);
            }

            if (isset($this->_filters[constant( $constantName)])) {
                return $this->_filters[constant($constantName)];
            } else {
                return null;
            }
        }

        // remove filter
        if ($methodName == 'remove') {
            $filterName = str_replace('remove', '', $method);

            if (false === $constantName = $this->_getConstantName($filterName)) {
                throw new Green_Filters_Exception("unknown '{$this->_className}' name = '{$filterName}'", Green_Filters_Exception::UNKNOWN_FILTER);
            }

            if (isset($this->_filters[constant($constantName)])) {
                unset($this->_filters[constant($constantName)]);
            }

            return $this;
        }

        // set filter
        $filterName = substr($method, 3);
        if (false === $constantName = $this->_getConstantName($filterName)) {
            throw new Green_Filters_Exception("unknown '{$this->_className}' name = '{$filterName}'", Green_Filters_Exception::UNKNOWN_FILTER);
        }

        $this->_filters[constant($constantName)]    = (isset($params[0])) ? $params[0] : false;

        return $this;
    }

    /**
     *
     * test and return constant name
     * @param $filterName
     * @return bool|string
     */
    protected function _getConstantName($filterName) {
        $filterName = preg_replace('/[A-Z]/', '_$0', $filterName);

        $filterName = strtoupper($filterName);

        $filterName = mb_substr($filterName, 1);

        if (defined('static::' . $filterName)) {
            return 'static::' . $filterName;
        } else {
            return false;
        }
    }

    /**
     * @static
     * @param $filterName
     * @return bool|string
     */
    public static function testConstant($filterName) {
        $filterName = preg_replace('/[A-Z]/', '_$0', $filterName);

        $filterName = strtoupper($filterName);

        $filterName = mb_substr($filterName, 1);

        if (defined('static::' . $filterName)) {
            return 'static::' . $filterName;
        } else {
            return false;
        }
    }


    /**
     * @param $filterName
     * @param $value
     * @return \BisApi_FiltersAbstract
     */
    public function setFilter($filterName, $value) {
        $methodName = 'set' . $filterName;
        $this->$methodName($value);
        return $this;
    }

    /**
     * @return bool
     */
    public function isEmpty() {
        if (empty($this->_filters)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * merge with other filters
     * @param Green_FiltersAbstract $filters
     */
    public function merge(Green_FiltersAbstract $filters) {
        foreach ($filters as $name => $value) {
            $methodName = 'set' . $name;
            $this->$methodName($value);
        }

        return $this;
    }

    /**
     * @return array
     */
    public function toArray() {
        return $this->_filters;
    }

    /**
     * @see Iterator::current()
     * @return mixed
     */
    public function current() {
        $var = current($this->_filters);
        return $var;
    }

    /**
     *
     * @see Iterator::next()
     */
    public function next() {
        $var = next($this->_filters);
        return $var;
    }

    /**
     *
     * @see Iterator::key()
     */
    public function key() {
        $var = key($this->_filters);
        return $var;
    }

    /**
     *
     * @see Iterator::valid()
     */
    public function valid() {
        $var = $this->current() !== false;
        return $var;
    }

    /**
     *
     * @see Iterator::rewind()
     */
    public function rewind() {
        reset($this->_filters);
    }
}